﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.IO;

namespace HexViewer_WF
{

    public enum VIEW_MODE
    {
        BYTE=0,
        INT=1,
        HALF_FLOAT=2,
        SINGLE_FLOAT=3
    }

    public partial class Main : Form
    {  
        public Main()
        {
            InitializeComponent();
            rbByte.CheckedChanged += (s,e)=>_ChangeViewMode(VIEW_MODE.BYTE);
            rbHalfFloat.CheckedChanged += (s,e)=>_ChangeViewMode(VIEW_MODE.HALF_FLOAT);
            rbInt.CheckedChanged += (s,e)=>_ChangeViewMode(VIEW_MODE.INT);
            rbSingleFloat.CheckedChanged += (s,e)=>_ChangeViewMode(VIEW_MODE.SINGLE_FLOAT);

            rbByte.Checked = true;
        }

        protected OpenFileDialog _ofd = new OpenFileDialog()
        {
            Title="Select File",
        };

        public Stream CurrentStream;

        protected string _curViewSet = "default";
        protected VIEW_MODE vMode = VIEW_MODE.BYTE;

        //加入控制是否继续的全局变量，防止进入死循环
        protected bool _canContinue = true;

        public void OpenFile(string filePath)
        {
            if (cbUnlockFile.Checked)
            {
                CurrentStream = new MemoryStream(File.ReadAllBytes(filePath));
            }
            else
            {
                CurrentStream = new FileStream(filePath, FileMode.Open);
            }

            lbFileName.Text = filePath;
            RefreshView();
        }

        public void CloseFile()
        {
            if(CurrentStream != null)
            {
                try
                {
                    CurrentStream.Dispose();
                    CurrentStream.Close();
                }catch(Exception e)
                {
                    Console.WriteLine("CloseFile:" + e.Message);
                }finally
                {
                    CurrentStream = null;
                }
            }
        }

        #region 各个表示方法的具体计算公式

        protected void _DefaultRefreshView(int size ,int offset)
        {
            int linesCount = size / 16;
            int remainCount = size % 16;

            CurrentStream.Position = offset;

            for (int i = 0; i < linesCount; i++)
            {
                string lineStr = "0x" + (offset + i * 16).ToString("X8");
                for (int j = 0; j < 16; j++)
                {
                    lineStr += string.Format(" {0:X2} ", CurrentStream.ReadByte());
                }

                view.Text += lineStr + "\n";
            }

            if (remainCount > 0)
            {
                string remainStr = "0x" + (offset + linesCount * 16).ToString("X8");
                for (int j = 0; j < remainCount; j++)
                {
                    remainStr += string.Format(" {0:X2} ", CurrentStream.ReadByte());
                }

                view.Text += remainStr + "\n";
            }
        }

        protected void _HalfFloatRefreshView(int size, int offset)
        {
            int linesCount = size / 16;

            CurrentStream.Position = offset;

            for (int i = 0; i < linesCount; i++)
            {
                string lineStr = "0x" + (offset + i * 16).ToString("X8");
                byte[] bs = new byte[2];
                for (int j = 0; j < 8; j++)
                {
                    if (CurrentStream.Read(bs,  0, 2) == 2)
                    {
                        lineStr += string.Format(" {0:F4} ", FloatHelper.HalfToFloat(bs)).PadLeft(8);
                    }else
                    {
                        Console.WriteLine(string.Format("read fail on {0}", offset + i * 8 + j * 2));
                    }
                }

                view.Text += lineStr + "\n";
            }
        }

        protected void _SingleFloatRefreshView(int size = 100,int offset = 0, int colItemCount = 4)
        {
            int lineBytes = colItemCount * 4;
            int linesCount = size / lineBytes;
            int remainCount = size % lineBytes;

            BinaryReader br = new BinaryReader(CurrentStream);
            br.BaseStream.Position = offset;

            for (int i = 0; i < linesCount; i++)
            {
                string lineStr = "0x" + (offset + i * lineBytes).ToString("X8");
                for (int j = 0; j < colItemCount; j++)
                {
                    float f = br.ReadSingle();
                    string col = "";
                    if( Math.Abs(f)<10)
                    {
                        col = $" {f:F4} ";
                    }
                    else
                    {
                        col = $" {f:E4} ";
                    }

                    col = col.PadLeft(16);

                    lineStr += col;
                }

                view.Text += lineStr + "\n";
            }

            br = null;
        }

        protected void _Int32RefreshView(int size = 100, int offset = 0, int colItemCount = 4)
        {
            int lineBytes = colItemCount * 4;
            int linesCount = size / lineBytes;
            int remainCount = size % lineBytes;

            BinaryReader br = new BinaryReader(CurrentStream);
            br.BaseStream.Position = offset;

            for (int i = 0; i < linesCount; i++)
            {
                string lineStr = "0x" + (offset + i * lineBytes).ToString("X8");
                for (int j = 0; j < colItemCount; j++)
                {
                    int f = br.ReadInt32();
                    string col = $" {f} ";

                    col = col.PadLeft(16);

                    lineStr += col;
                }

                view.Text += lineStr + "\n";
            }

            br = null;
        }

        #endregion

        /// <summary>
        /// 刷新视图
        /// </summary>
        public void RefreshView()
        {
            if(CurrentStream == null)
            {
                return;
            }

            int size = (int)loadSize.Value;
            int offset = (int)startOffset.Value;

            if(size<=0 || offset+size> CurrentStream.Length)
            {
                return;
            }

            view.Text = null;

            switch (vMode)
            {
                default:
                    _DefaultRefreshView(size, offset);
                    break;
                case VIEW_MODE.HALF_FLOAT:
                    _HalfFloatRefreshView(size, offset);
                    break;
                case VIEW_MODE.SINGLE_FLOAT:
                    _SingleFloatRefreshView(size, offset);
                    break;
                case VIEW_MODE.INT:
                    _Int32RefreshView(size, offset);
                    break;
            }
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            lbFileName.Text = "None";
            if (_ofd.ShowDialog() == DialogResult.OK)
            {
                if(File.Exists(_ofd.FileName))
                {
                    OpenFile(_ofd.FileName);
                }
            }
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            RefreshView();
        }


        private void viewSet_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            CheckedListBox clb = sender as CheckedListBox;
            //勾选原选项
            if (clb.CheckedIndices.Count > 0 && clb.CheckedIndices[0] == e.Index && _canContinue)
            {
                e.NewValue = CheckState.Checked;
            }
            //第一次勾选
            else if (clb.CheckedIndices.Count == 0)
            {
                _canContinue = true;
            }
            //勾选新选项
            else if (clb.CheckedIndices.Count > 0 && clb.CheckedIndices[0] != e.Index)
            {
                _canContinue = false;
                clb.SetItemChecked(clb.CheckedIndices[0], false);
                //通过索引引用勾选的条目
                //string newViewSet = viewSet.CheckedItems[0].ToString();
                //if (newViewSet != _curViewSet)
                //{
                //    RefreshView();
                //}
            }
            //勾选新选项后重置CanContinue
            else
            {
                _canContinue = true;
            }
        }

        private void _ChangeViewMode(VIEW_MODE newMode)
        {
            if(vMode!=newMode)
            {
                vMode = newMode;
                RefreshView();
            }
        }
    }
}
